use serde_json::{Value, Result};
use std::collections::HashMap;
use std::error::Error;
use std::time::Duration;
use std::thread::sleep;

const DEFAULT_UPDATE_TIMEOUT: u32 = 3;                          // 3 seconds

const DEFAULT_RETRY_COUNT: u32 = 3;                             // retry 3 times

const DEFAULT_RETRY_WAIT: u32 = 3;                              // wait 3 seconds between retries

struct DataSourceHttpDir {
    urls: Vec<str>,
    update_timeout: u32,
    retry_count: u32,
    retry_wait: u32,
}

impl DataSource for DataSourceHttpDir {
    fn new(name: str, param: Value) {
        if param.get("url").is_some() {
            if param.get("urls").is_some() {
                return Err(format!("no urls in {:?}", source).into());
            }
            if !param["url"].starts_with("http://") && !param["url"].starts_with("https://") {
                return Err(format!("invalid url in {:?}", source).into());
            }
            let urls = vec![param["url"].clone()];
        } else if param.get("urls").is_some() {
            if param.get("urls").is_some() {
                return Err(format!("no url in {:?}", source).into());
            }
            if param.get("urls").iter().any(|&x| {!x.starts_with("http://") && !x.starts_with("https://")}) {
                return Err(format!("invalid urls in {:?}", source).into());
            }
            let urls = param["urls"].clone();
        } else {
            return Err(format!("no url in {:?}", source).into());
        }

        let update_timeout = match param.get("update-timeout") {
            Some(value) => {
                if let Some(x) = value.as_u32() {
                    x as u32
                } else {
                    return Err("invalid value for \"update-timeout\" property");
                }
            },
            None => DEFAULT_UPDATE_TIMEOUT,
        };

        let retry_count = match param.get("retry-count") {
            Some(value) => {
                if let Some(x) = value.as_u32() {
                    x as u32
                } else {
                    return Err("invalid value for \"retry-count\" property");
                }
            },
            None => DEFAULT_RETRY_COUNT,
        };

        let retry_wait = match param.get("retry-wait") {
            Some(value) => {
                if let Some(x) = value.as_u32() {
                    x as u32
                } else {
                    return Err("invalid value for \"retry-wait\" property".to_string());
                }
            },
            None => DEFAULT_RETRY_WAIT,
        };


    }

    fn get_name(&self) -> str {

    }


    fn get_disk_cache_info(&self) -> CacheInfo {

    }

    fn get_update_timeout(&self) -> u32 {
        self.update_timeout
    }

    fn get_retry_count(&self) -> u32 {
        self.retry_count
    }
    
    fn get_retry_wait(&self) -> u32 {
        self.retry_wait
    }

    fn get_site_data(&self, mirror_name_list: Vec<&str>) -> Result<Value, DataSourceError> {
        let retry_count = self.cfg.get_retry_count();
        for _ in 0..=retry_count {
            let mut last_error = None;
            for entry in htmllistparse::fetch_listing(&self._url, timeout = self.cfg.get_update_timeout())?.1 {
                if entry.name.ends_with(".json") {
                    let name = entry.name[..entry.name.len() - 5].to_string();
                    if mirror_name_list.is_none() || mirror_name_list.as_ref().unwrap().contains(&name) {
                        ret.insert(name, None);
                    }
                }
            }
            break;
        }
        if let Some(last_error) = last_error {
            return Err(SourceError::new(format!("failed to access URL \"{}\", {}", &self.url, last_error)));
        }
        for name in ret.keys() {
            let fn_name = format!("{}.json", name);
            let url = std::path::Path::new(&fn_name).join(&self.cfg.base_url);
            // TODO: do something with url
        }
        Ok(ret)
    }
}

impl Config {
    fn get_retry_count(&self) -> u32 {
        self.retry_count
    }

    fn get_update_timeout(&self) -> Duration {
        self.update_timeout
    }

    fn get_retry_wait(&self) -> Duration {
        self.retry_wait
    }
}
